package com.jeeplus.common.utils;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jeeplus.common.config.JacksonObjectMapper;

/**
 * 复制工具
 * @author rubekid
 * @date 2016年10月29日
 */
public class BeanUtils {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(BeanUtils.class);

	/**
	 * 获取字段值
	 * @param object
	 * @param fieldName
	 * @return
	 * @throws NoSuchFieldException
	 */
	public static Object getFieldValue(Object object, String fieldName)
			throws NoSuchFieldException {
		Field field = getDeclaredField(object, fieldName);
		if (!(field.isAccessible())) {
			field.setAccessible(true);
		}

		Object result = null;
		try {
			result = field.get(object);
		} catch (IllegalAccessException e) {
			LOGGER.error("不可能抛出的异常{}", e.getMessage());
		}
		return result;
	}

	/**
	 * 设置字段值
	 * @param object
	 * @param fieldName
	 * @param value
	 * @throws NoSuchFieldException
	 */
	public static void setFieldValue(Object object, String fieldName,
			Object value) throws NoSuchFieldException {
		Field field = getDeclaredField(object, fieldName);
		if (!(field.isAccessible()))
			field.setAccessible(true);
		try {
			field.set(object, value);
		} catch (IllegalAccessException e) {
			LOGGER.error("不可能抛出的异常:{}", e.getMessage());
		}
	}

	/**
	 * 获取字段值
	 * @param object
	 * @param fieldName
	 * @return
	 * @throws NoSuchFieldException
	 */
	public static Field getDeclaredField(Object object, String fieldName)
			throws NoSuchFieldException {
		Assert.notNull(object);
		return getDeclaredField(object.getClass(), fieldName);
	}

	/**
	 *  获取字段值
	 * @param clazz
	 * @param fieldName
	 * @return
	 * @throws NoSuchFieldException
	 */
	@SuppressWarnings("rawtypes")
	public static Field getDeclaredField(Class clazz, String fieldName)
			throws NoSuchFieldException {
		Assert.notNull(clazz);
		Assert.hasText(fieldName);
		Class superClass = clazz;
		try {
			return superClass.getDeclaredField(fieldName);
		} catch (NoSuchFieldException localNoSuchFieldException) {
			do
				superClass = superClass.getSuperclass();
			while (superClass != Object.class);

			throw new NoSuchFieldException("No such field: " + clazz.getName()
					+ '.' + fieldName);
		}
	}
	
	/**
	 * 数据转换
	 * @param list
	 * @param clazz
	 * @param ignoreProperties
	 * @return
	 */
	public static <T> List<T> transfer(List<?> list, Class<T> clazz, String ... ignoreProperties ){
		if(list == null){
			return null;
		}
		List<T> items = new ArrayList<T>();
	
		for(int i=0; i< list.size(); i++){
			items.add(transfer(list.get(i), clazz, ignoreProperties));
		}
	
		return items;	
	}
	
	/**
	 * 对象转换
	 * @param object
	 * @param clazz
	 * @param ignoreProperties
	 * @return
	 */
	public static <T> T transfer(Object object, Class<T> clazz, String ... ignoreProperties){
		if(object == null){
			return null;
		}
		try {
			T item = clazz.newInstance();
			org.springframework.beans.BeanUtils.copyProperties(object, item, ignoreProperties);
			return item;
		} catch (InstantiationException | IllegalAccessException ex) {
			LOGGER.error(ex.getMessage(), ex);
		}
		return null;
	}
	
	/**
	 * 更新target字段值
	 * @param source
	 * @param target
	 */
	public static  void update(Object target, Object source){
		Assert.notNull(target, "Target must not be null");
		Assert.notNull(source, "Source must not be null");

		Class<?> actualEditable = target.getClass();

		PropertyDescriptor[] targetPds = org.springframework.beans.BeanUtils.getPropertyDescriptors(actualEditable);
		for (PropertyDescriptor targetPd : targetPds) {
			Method writeMethod = targetPd.getWriteMethod();
			if (writeMethod != null ) {
				PropertyDescriptor sourcePd = org.springframework.beans.BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getName());
				if (sourcePd != null) {
					Method readMethod = sourcePd.getReadMethod();
					if (readMethod != null &&
							ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
						try {
							if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
								readMethod.setAccessible(true);
							}
							Object value = readMethod.invoke(source);
							//值为null不更新
							if(value == null){
								continue ;
							}
							if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
								writeMethod.setAccessible(true);
							}
							writeMethod.invoke(target, value);
						}
						catch (Throwable ex) {
							throw new FatalBeanException(
									"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
						}
					}
				}
			}
		}
	}
	
	/**
	 * 拷贝属性
	 * @param source
	 * @param target
	 * @throws BeansException
	 */
	public static void copyProperties(Object source, Object target) throws BeansException {
		org.springframework.beans.BeanUtils.copyProperties(source, target);
	}

	/**
	 * 拷贝属性
	 * @param source
	 * @param target
	 * @param editable
	 * @throws BeansException
	 */
	public static void copyProperties(Object source, Object target, Class<?> editable) throws BeansException {
		org.springframework.beans.BeanUtils.copyProperties(source, target, editable);
	}

	/**
	 * 拷贝属性
	 * @param source
	 * @param target
	 * @param ignoreProperties
	 * @throws BeansException
	 */
	public static void copyProperties(Object source, Object target, String... ignoreProperties) throws BeansException {
		org.springframework.beans.BeanUtils.copyProperties(source, target, ignoreProperties);
	}
	

	/**
	 * 转字符串
	 * @param object
	 * @return
	 */
	public static String toJSONString(Object object) {
		ObjectMapper objectMapper = new JacksonObjectMapper();
		try {
			return objectMapper.writeValueAsString(object);
		} catch (JsonProcessingException ex) {
			LOGGER.error(ex.getMessage(), ex);
		}
		return "";
	}
	
	/**
	 * 转对象
	 * @param value
	 * @param clazz
	 * @return
	 * @throws IOException
	 */
	public static <T> T parse(String value, Class<T> clazz){
		ObjectMapper objectMapper = new JacksonObjectMapper();
		try {
			return objectMapper.readValue(value, clazz);
		} catch (IOException ex) {
			LOGGER.error(ex.getMessage(), ex);
		}
		return null;
	}
}
